package com.gzc.just.play.last.war.sceneserver.integration;

import com.gzc.just.play.last.war.sceneserver.entity.Player;
import com.gzc.just.play.last.war.sceneserver.entity.PlayerAttribute;
import com.gzc.just.play.last.war.sceneserver.repository.PlayerRepository;
import com.gzc.just.play.last.war.sceneserver.service.PlayerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cache.CacheManager;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 玩家服务集成测试
 * 
 * 测试PlayerService的业务逻辑和缓存功能
 * 作为Phase 4.3数据库持久化集成的一部分
 */
@SpringBootTest
@ActiveProfiles("test")
@Transactional
@DisplayName("玩家服务集成测试")
public class PlayerServiceIntegrationTest {
    
    @Autowired
    private PlayerService playerService;
    
    @Autowired
    private PlayerRepository playerRepository;
    
    @Autowired
    private CacheManager cacheManager;
    
    private Player testPlayer;
    
    @BeforeEach
    void setUp() {
        // 清空测试数据
        playerRepository.deleteAll();
        
        // 创建测试玩家
        testPlayer = playerService.createPlayer("testuser", "hashedPassword", "salt123", "Test Player");
    }
    
    @Test
    @DisplayName("创建玩家")
    void testCreatePlayer() {
        // 验证玩家创建成功
        assertNotNull(testPlayer.getPlayerId(), "玩家ID应该不为空");
        assertEquals("testuser", testPlayer.getUsername(), "用户名应该匹配");
        assertEquals("Test Player", testPlayer.getNickname(), "昵称应该匹配");
        assertEquals(1, testPlayer.getLevel(), "初始等级应该是1");
        assertEquals(0L, testPlayer.getExperience(), "初始经验应该是0");
        
        // 验证玩家属性创建成功
        Optional<Player> createdPlayer = playerService.getPlayerById(testPlayer.getPlayerId());
        assertTrue(createdPlayer.isPresent(), "应该能找到创建的玩家");
    }
    
    @Test
    @DisplayName("创建重复用户名的玩家应该失败")
    void testCreateDuplicatePlayer() {
        // 尝试创建相同用户名的玩家
        assertThrows(RuntimeException.class, () -> {
            playerService.createPlayer("testuser", "hashedPassword2", "salt456", "Test Player 2");
        }, "应该抛出异常");
    }
    
    @Test
    @DisplayName("根据ID获取玩家")
    void testGetPlayerById() {
        Optional<Player> foundPlayer = playerService.getPlayerById(testPlayer.getPlayerId());
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getUsername(), foundPlayer.get().getUsername(), "用户名应该匹配");
        
        // 验证缓存
        assertNotNull(cacheManager.getCache("players").get(testPlayer.getPlayerId()), "应该缓存玩家数据");
    }
    
    @Test
    @DisplayName("根据用户名获取玩家")
    void testGetPlayerByUsername() {
        Optional<Player> foundPlayer = playerService.getPlayerByUsername("testuser");
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getPlayerId(), foundPlayer.get().getPlayerId(), "玩家ID应该匹配");
        
        // 验证缓存
        assertNotNull(cacheManager.getCache("players").get("testuser"), "应该缓存玩家数据");
    }
    
    @Test
    @DisplayName("玩家登录")
    void testLoginPlayer() {
        // 玩家登录
        Player loggedInPlayer = playerService.loginPlayer(testPlayer.getPlayerId());
        
        assertTrue(loggedInPlayer.getIsOnline(), "玩家应该在线");
        assertNotNull(loggedInPlayer.getLastLoginTime(), "最后登录时间应该不为空");
        assertEquals(1, loggedInPlayer.getLoginCount(), "登录次数应该是1");
        
        // 再次登录
        Player loggedInPlayer2 = playerService.loginPlayer(testPlayer.getPlayerId());
        assertEquals(2, loggedInPlayer2.getLoginCount(), "登录次数应该是2");
    }
    
    @Test
    @DisplayName("玩家登出")
    void testLogoutPlayer() {
        // 先登录
        playerService.loginPlayer(testPlayer.getPlayerId());
        
        // 玩家登出
        playerService.logoutPlayer(testPlayer.getPlayerId());
        
        // 验证登出状态
        Optional<Player> loggedOutPlayer = playerService.getPlayerById(testPlayer.getPlayerId());
        assertTrue(loggedOutPlayer.isPresent(), "应该能找到玩家");
        assertFalse(loggedOutPlayer.get().getIsOnline(), "玩家应该离线");
        assertNotNull(loggedOutPlayer.get().getLastLogoutTime(), "最后登出时间应该不为空");
    }
    
    @Test
    @DisplayName("被封禁玩家登录应该失败")
    void testBannedPlayerLogin() {
        // 封禁玩家
        LocalDateTime banEndTime = LocalDateTime.now().plusDays(1);
        playerService.banPlayer(testPlayer.getPlayerId(), "测试封禁", banEndTime);
        
        // 尝试登录
        assertThrows(RuntimeException.class, () -> {
            playerService.loginPlayer(testPlayer.getPlayerId());
        }, "应该抛出异常");
    }
    
    @Test
    @DisplayName("增加玩家经验")
    void testAddExperience() {
        // 增加经验
        Player updatedPlayer = playerService.addExperience(testPlayer.getPlayerId(), 500L);
        
        assertEquals(500L, updatedPlayer.getExperience(), "经验应该是500");
        assertEquals(1, updatedPlayer.getLevel(), "等级应该仍然是1");
        
        // 再增加600经验，应该升级到2级
        Player upgradedPlayer = playerService.addExperience(testPlayer.getPlayerId(), 600L);
        assertEquals(1100L, upgradedPlayer.getExperience(), "经验应该是1100");
        assertEquals(2, upgradedPlayer.getLevel(), "等级应该是2");
    }
    
    @Test
    @DisplayName("封禁玩家")
    void testBanPlayer() {
        LocalDateTime banEndTime = LocalDateTime.now().plusDays(1);
        Player bannedPlayer = playerService.banPlayer(testPlayer.getPlayerId(), "测试封禁", banEndTime);
        
        assertTrue(bannedPlayer.getIsBanned(), "玩家应该被封禁");
        assertEquals("测试封禁", bannedPlayer.getBanReason(), "封禁原因应该匹配");
        assertEquals(banEndTime, bannedPlayer.getBanEndTime(), "封禁结束时间应该匹配");
        assertFalse(bannedPlayer.getIsOnline(), "被封禁玩家应该离线");
    }
    
    @Test
    @DisplayName("解除玩家封禁")
    void testUnbanPlayer() {
        // 先封禁玩家
        LocalDateTime banEndTime = LocalDateTime.now().plusDays(1);
        playerService.banPlayer(testPlayer.getPlayerId(), "测试封禁", banEndTime);
        
        // 解除封禁
        Player unbannedPlayer = playerService.unbanPlayer(testPlayer.getPlayerId());
        
        assertNotNull(unbannedPlayer, "应该能找到玩家");
        assertFalse(unbannedPlayer.getIsBanned(), "玩家应该未被封禁");
        assertNull(unbannedPlayer.getBanReason(), "封禁原因应该为空");
        assertNull(unbannedPlayer.getBanEndTime(), "封禁结束时间应该为空");
    }
    
    @Test
    @DisplayName("获取在线玩家列表")
    void testGetOnlinePlayers() {
        // 登录测试玩家
        playerService.loginPlayer(testPlayer.getPlayerId());
        
        // 创建另一个玩家并登录
        Player anotherPlayer = playerService.createPlayer("testuser2", "hashedPassword2", "salt456", "Test Player 2");
        playerService.loginPlayer(anotherPlayer.getPlayerId());
        
        List<Player> onlinePlayers = playerService.getOnlinePlayers();
        
        assertEquals(2, onlinePlayers.size(), "应该有2个在线玩家");
    }
    
    @Test
    @DisplayName("获取被封禁玩家列表")
    void testGetBannedPlayers() {
        // 封禁测试玩家
        playerService.banPlayer(testPlayer.getPlayerId(), "测试封禁", LocalDateTime.now().plusDays(1));
        
        List<Player> bannedPlayers = playerService.getBannedPlayers();
        
        assertEquals(1, bannedPlayers.size(), "应该有1个被封禁的玩家");
        assertEquals(testPlayer.getPlayerId(), bannedPlayers.get(0).getPlayerId(), "应该找到被封禁的玩家");
    }
    
    @Test
    @DisplayName("统计在线玩家数量")
    void testCountOnlinePlayers() {
        // 登录测试玩家
        playerService.loginPlayer(testPlayer.getPlayerId());
        
        long onlineCount = playerService.countOnlinePlayers();
        
        assertEquals(1, onlineCount, "应该有1个在线玩家");
    }
    
    @Test
    @DisplayName("统计被封禁玩家数量")
    void testCountBannedPlayers() {
        // 封禁测试玩家
        playerService.banPlayer(testPlayer.getPlayerId(), "测试封禁", LocalDateTime.now().plusDays(1));
        
        long bannedCount = playerService.countBannedPlayers();
        
        assertEquals(1, bannedCount, "应该有1个被封禁的玩家");
    }
    
    @Test
    @DisplayName("删除玩家")
    void testDeletePlayer() {
        // 验证玩家存在
        Optional<Player> playerBefore = playerService.getPlayerById(testPlayer.getPlayerId());
        assertTrue(playerBefore.isPresent(), "玩家应该存在");
        
        // 删除玩家
        playerService.deletePlayer(testPlayer.getPlayerId());
        
        // 验证玩家不存在
        Optional<Player> playerAfter = playerService.getPlayerById(testPlayer.getPlayerId());
        assertFalse(playerAfter.isPresent(), "玩家应该不存在");
    }
    
    @Test
    @DisplayName("删除不存在的玩家应该失败")
    void testDeleteNonExistentPlayer() {
        assertThrows(RuntimeException.class, () -> {
            playerService.deletePlayer(999999L);
        }, "应该抛出异常");
    }
}